# include "utils.h"

namespace mslite {
    
double getAvg(std::vector<double> vals, int removes) {
    sort(vals.begin(), vals.end());
    double avg = 0;
    int cnt = 0;
    for (int i=removes; i<vals.size()-removes; i++) {
        avg += vals[i];
        cnt++;
    }
    return (avg / cnt);
}

int readSrcCode(const std::string filename, std::string &srcCode) {
    std::stringstream ss;
    std::ifstream clFile(filename, std::ios::in);
    ss << clFile.rdbuf();
    clFile.close();
    srcCode = std::move(ss.str());
    return 0;
}

// lhs - m*n,  rhs - n*k
std::vector<float> matmul(std::vector<float> &lhs, std::vector<float> &rhs, int m, int n, int k) {
    std::vector<float> res(m*k);
    for (int y = 0; y < m; ++y) {
        for (int x = 0; x < k; ++x) {
            float sum = 0.0f;
            for (int i = 0; i < n; ++i) {
                sum += lhs[y * n + i] * rhs[i * k + x];
            }
            res[y * k + x] = sum;
        }
    }
    return std::move(res);
}

// eg: ogroup=1, 128*6*6*128 --> 32*6*6*32*4*4
//     ogroup=2, 128*6*6*128 --> 16*6*6*32*2*4*4
//     ogroup=4, 128*6*6*128 --> 8*6*6*32*4*4*4
std::vector<float> ReorderOHWIToI4O4(const std::vector<float> &weight, std::vector<int> shape, int ogroup) {
    std::vector<float> res(weight.size());
    int oc = shape[0], ic = shape[3], kh = shape[1], kw = shape[2];
    int oc_slice_grp = oc / (4*ogroup), ic_slice = ic / 4, cnt = 0;
    for (int osg=0; osg<oc_slice_grp; osg++) {
        for (int y=0; y<kh; y++) {
            for (int x=0; x<kw; x++) {
                for (int is=0; is<ic_slice; is++) {
                    for (int og=0; og<ogroup; og++) {
                        for (int j=0; j<4; j++) {
                            for (int i=0; i<4; i++) {
                                int cur_ic = is*4 + j;
                                int cur_oc = (osg * ogroup + og) * 4 + i;
                                res[cnt++] = weight[((cur_oc*6+y)*6+x)*ic+cur_ic];
                            }
                        }
                    }
                }
            }
        }
    }
    // return std::move(res);
    return res;
}

cl_mem createImage2d(cl_context context, cl_channel_type type, int width, int height, void* data, cl_int *status) {
    cl_image_desc desc;
    desc.image_type = CL_MEM_OBJECT_IMAGE2D;
    desc.image_width = width;
    desc.image_height = height;
    desc.image_depth = 0;
    desc.image_row_pitch = 0;
    desc.image_slice_pitch = 0;
    desc.num_mip_levels = 0;
    desc.num_samples = 0;
    desc.buffer = nullptr;

    cl_image_format format;
    format.image_channel_order = CL_RGBA;
    format.image_channel_data_type = type;

    cl_mem_flags flags = CL_MEM_READ_WRITE;
    if (data != nullptr) 
        flags |= CL_MEM_COPY_HOST_PTR;
    else 
        flags |= CL_MEM_ALLOC_HOST_PTR;

    cl_mem texture = clCreateImage(context, flags, &format, &desc, data, status);
    return texture;
}

double profileTime(cl_event profEvent, bool log) {
    cl_int status;
    cl_ulong evStartTime = static_cast<cl_ulong>(0);
    cl_ulong evEndTime = static_cast<cl_ulong>(0);
    status = clGetEventProfilingInfo(profEvent, CL_PROFILING_COMMAND_START, sizeof(cl_ulong), &evStartTime, NULL);
    status = clGetEventProfilingInfo(profEvent, CL_PROFILING_COMMAND_END, sizeof(cl_ulong), &evEndTime, NULL);
    if (log)
        std::cout << "Run Time: " << static_cast<double>(evEndTime - evStartTime) / 1e6 << "ms" << std::endl;
    return (static_cast<double>(evEndTime - evStartTime) / 1e6);
}


}
